﻿using Firefly.Framework.ContentManagement;
using Firefly.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Firefly.Framework.Components.Sky
{
    public class PerlinCloudsShader : ShaderProxy, IShaderProxy
    {
        private QuadRenderer _quadRenderer;

        public Vector4 SunlightColor
        {
            get { return Effect.Parameters["SunColor"].GetValueVector4(); }
            set { Effect.Parameters["SunColor"].SetValue(value); }
        }
        public float Time
        {
            get { return Effect.Parameters["Time"].GetValueSingle(); }
            set { Effect.Parameters["Time"].SetValue(value / InverseCloudVelocity); }
        }
        public float NumberOfTiles
        {
            get { return Effect.Parameters["NumberOfTiles"].GetValueSingle(); }
            set { Effect.Parameters["NumberOfTiles"].SetValue(value); }
        }
        public float CloudCover
        {
            get { return Effect.Parameters["CloudCover"].GetValueSingle(); }
            set { Effect.Parameters["CloudCover"].SetValue(value); }
        }
        public float CloudSharpness
        {
            get { return Effect.Parameters["CloudSharpness"].GetValueSingle(); }
            set { Effect.Parameters["CloudSharpness"].SetValue(value); }
        }
        public float InverseCloudVelocity { get; set; }
        public Texture2D Texture
        {
            get { return Effect.Parameters["Texture"].GetValueTexture2D(); }
            set { Effect.Parameters["Texture"].SetValue(value); }
        }

        public PerlinCloudsShader(PerlinClouds parent)
            : base(parent)
        {
        }

        private void GenerateTexture()
        {
            int[] texture = 
            { 
                151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
                142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
                203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68,
                175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 
                220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54,  65, 25, 63, 161,  1, 216, 80, 73, 209, 
                76, 132, 187, 208,  89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 
                186,  3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 
                206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152,  2, 44, 154, 163,
                70, 221, 153, 101, 155, 167,  43, 172, 9, 129, 22, 39, 253,  19, 98, 108, 110, 79, 113, 224, 
                232, 178, 185,  112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 
                241,  81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214,  31, 181, 199, 106, 157, 184,  84, 204, 
                176, 115, 121, 50, 45, 127,  4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 
                141, 128, 195, 78, 66, 215, 61, 156, 180
            };

            int[] gradientValues = 
            { 
                1, 1, 0, -1, 1, 0, 
                1, -1, 0, -1, -1, 0, 
                1, 0, 1, -1, 0, 1, 
                1, 0, -1, -1, 0, -1, 
                0, 1, 1, 0, -1, 1, 
                0, 1, -1, 0, -1, -1, 
                1, 1, 0, 0, -1, 1, 
                -1, 1, 0, 0, -1, -1
            };

            Texture = new Texture2D(GraphicsService.Instance.GraphicsDevice, 256, 256, 1, TextureUsage.None, SurfaceFormat.Color);
            byte[] pixels;
            pixels = new byte[256 * 256 * 4];

            for (int i = 0; i < 256; i++)
            {
                for (int j = 0; j < 256; j++)
                {
                    int offset = ((i * 256) + j) * 4;
                    byte value = (byte)texture[(j + texture[i]) & 0xFF];
                    pixels[offset + 1] = (byte)((gradientValues[value & 0x0F] * 64) + 64);
                    pixels[offset + 2] = (byte)((gradientValues[value & 0x0F + 1] * 64) + 64);
                    pixels[offset + 3] = (byte)((gradientValues[value & 0x0F + 2] * 64) + 64);
                    pixels[offset] = value;
                }
            }

            Texture.SetData<byte>(pixels);
        }

        #region IShaderProxy Members

        public override void Draw(GameTime gameTime)
        {
            Effect.Begin();

            foreach (EffectPass pass in Effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                _quadRenderer.Draw(gameTime);
                pass.End();
            }

            Effect.End();
        }

        #endregion

        #region IHasContent

        public override void LoadContent()
        {
            EffectName = "Effects/Skies/PerlinClouds";
            Effect = ContentService.Instance.Load<Effect>(EffectName);
            Effect.CurrentTechnique = Effect.Techniques["Render"];
            GenerateTexture();
            _quadRenderer = new QuadRenderer((Parent as IUpdateableComponent).Game);
            _quadRenderer.LoadContent();
        }

        public override void UnloadContent()
        {
            ContentService.Instance.Release(EffectName);

            if (_quadRenderer != null)
            {
                _quadRenderer.Dispose();
                _quadRenderer = null;
            }
        }

        #endregion

        #region IDisposable Members

        ~PerlinCloudsShader()
        {
            Dispose(false);
        }

        #endregion
    }
}